import os
import queue
from datetime import datetime
from threading import Thread
from time import sleep

from src.lib.crc16 import crc16Bytes, crc16
from src.lib.errorno import g_errno_d


class BoxUpProtocols():

    rx_buff = queue.Queue()

    # PACK
    UP_PORT_HEAD = "UP^"
    UP_PACK_HEAD_1ST_BYTE = 0xA5
    UP_PACK_OPCODE_DATA = 0x5A
    UP_PACK_OPCODE_ABORT = 0xB6
    m_i_pack_id = 0
    m_i_pack_data_len = 0
    m_b_pack_data = bytes()
    m_b_pack_crc = bytes()
    UP_PORT_TAIL = "\r\n"

    # data
    m_s_pack_data = bytes()

    # up_file
    m_i_file_size = 0
    m_i_file_crc = 0
    m_i_send_size_cum = 0

    # up pack size
    m_i_pack_size = 128

    m_s_cmd_prefix = ""
    m_b_cmd_suffix = bytes()

    _success_tag = "UP^OK,0,"
    _fail_tag = "UP^FAIL,"

    def __init__(self, ui, port):
        super(BoxUpProtocols, self).__init__()
        self.ui = ui
        self.Port = port
        self.MainWindow = self.ui.MainWindow

    def uiSendReceive(self, text):
        self.Port.m_s_current_time = self.Port.getCurrentTime()
        self.Port.Dfu.uiForwardReceive(self.Port.getTimeStrAndText(text))

    def getData(self, redelivery, opcode):
        if not redelivery:
            self.m_i_pack_id = self.m_i_pack_id + 1
            if (self.m_i_pack_id > 255):
                self.m_i_pack_id = 0

        # print("pack_id", self.m_i_pack_id)
        self.m_i_pack_data_len = int(len(self.m_b_pack_data))

        pack_info = bytes([
            self.UP_PACK_HEAD_1ST_BYTE, opcode, self.m_i_pack_id,
            self.m_i_pack_data_len
        ])

        self.m_b_pack_crc = crc16Bytes(pack_info + self.m_b_pack_data, False)
        return ((self.UP_PORT_HEAD).encode('utf-8') + pack_info +
                self.m_b_pack_data + self.m_b_pack_crc)

    def flagRestart(self):
        self.m_i_pack_id = 0
        print("OTA flagRestart")

    # 获取当前升级进度(%)
    def getUpPercentage(self):
        return (self.m_i_send_size_cum / self.m_i_file_size) * 100

    def loadUpFile(self, file_path):
        self.file = open(file=file_path, mode='rb')
        self.m_i_file_crc = crc16(self.file.read(), False)
        self.m_i_file_size = os.path.getsize(file_path)
        self.m_i_send_size_cum = 0
        self.file.seek(0)

    def closeUpFile(self):
        self.file.close()
        self.m_i_send_size_cum = 0

    def readyNextPack(self):
        try:
            bytes_s = self.file.read(self.m_i_pack_size)
            self.m_b_pack_data = bytes_s
            self.m_i_send_size_cum += len(bytes_s)
        except Exception:
            return g_errno_d['GET']
        if len(bytes_s) == 0:
            return g_errno_d['INVAL_LEN']
        # read data completion
        return g_errno_d['OK']

    def BoxTaskStart(self):
        # 重置flag
        self.flagRestart()

        # 清空队列
        self.rx_buff.queue.clear()
        '''线程启动(无法双核运行)'''
        self.BoxUpThr = ""
        self.BoxUpThr = Thread(target=self.task_box_up, daemon=True)
        self.BoxUpThr.start()

    def task_box_up(self):
        version1 = ""
        version2 = ""
        print(datetime.now())
        ret = g_errno_d['OK']
        '''向下位机发送停止升级命令'''
        ret = self.__subTask_SendCmdAbort()
        '''发送查询版本命令'''
        if ret == g_errno_d['OK']:
            # 清空队列
            self.rx_buff.queue.clear()
            if self.MainWindow.chk_earUpBox.isChecked():
                ret, version_temp = self.__subTask_SendAt_Retry(
                    "AT^EVERSION?".encode(), 5, "CMD[VERSION] ", 0.1)
                i = version_temp.find("^VERSION:")
                if i != -1:
                    version1 = version_temp[i:]
            else:
                ret, version1 = self.__subTask_SendAt_Retry(
                    "AT^VERSION?".encode(), 5, "CMD[VERSION] ", 0.1)
        '''发送查询升级状态命令'''
        if ret == g_errno_d['OK']:
            # 清空队列
            self.rx_buff.queue.clear()
            print("AT^CHECKUP=%s,%d" % (self.m_i_file_crc, self.m_i_file_size))
            ret, data = self.__subTask_SendAt_Retry(("AT^CHECKUP=%s,%d" %
                                                    (self.m_i_file_crc, self.m_i_file_size)).encode(), 5,
                                                    "CMD[CHECKUP] ", 0)
        '''发送关闭log系统命令'''
        if (ret == g_errno_d['OK']):
            # 清空队列
            self.rx_buff.queue.clear()
            ret, data = self.__subTask_SendAt_Retry("AT^LOGCMD=0".encode(), 5,
                                                    "CMD[LOGCMD] ", 0)
        self.start_time = datetime.now()
        '''发送DFU数据命令'''
        if (ret == g_errno_d['OK']):
            # 清空队列
            self.rx_buff.queue.clear()
            ret = self.__subTask_SendUpData()

        if self.Port.Dfu.m_i_upgrade_flag == 0:
            ret = g_errno_d['FAIL']
        '''发送DFU升级结束'''
        if (ret == g_errno_d['OK']):
            sleep(0.1)
            # 清空队列
            self.rx_buff.queue.clear()
            ret = self.__subTask_SendUpEnd()

        time_s = str((datetime.now() - self.start_time).total_seconds())[:-3]
        self.uiSendReceive("发送完毕用时:" + time_s + "s")

        if (ret == g_errno_d['OK']):
            sleep(3)
        '''发送查询版本命令'''
        if (ret == g_errno_d['OK']):
            # 清空队列
            self.rx_buff.queue.clear()
            if (self.MainWindow.chk_earUpBox.isChecked()):
                self.__subTask_Fota_Wait_Finish()
                ret, version_temp = self.__subTask_SendAt_Retry(
                    "AT^EVERSION?".encode(), 2, "CMD[VERSION] ", 1)
                i = version_temp.find("^VERSION:")
                if (i != -1):
                    version2 = version_temp[i:]
            else:
                ret, version2 = self.__subTask_SendAt_Retry(
                    "AT^VERSION?".encode(), 5, "CMD[VERSION] ", 1)

        print(datetime.now())
        if (ret == g_errno_d['OK']):
            self.uiSendReceive("数据发送完毕")
            self.uiSendReceive("旧：\r\n" + version1 + "\r\n")
            self.uiSendReceive("新: \r\n" + version2 + "\r\n")
            if version1 == "" or version2 == "":
                self.Port.Dfu.portUpTaskStop("升级失败!", g_errno_d['FAIL'])
            else:
                version1 = version1.replace("\n", "")
                version2 = version2.replace("\n", "")
                if version1 == version2:
                    self.Port.Dfu.portUpTaskStop("新旧版本一致", g_errno_d['FAIL'])
                else:
                    self.Port.Dfu.portUpTaskStop("升级成功!", g_errno_d['OK'])
        else:
            '''发送开启log系统命令'''
            # 清空队列
            self.rx_buff.queue.clear()
            ret, data = self.__subTask_SendAt_Retry("AT^LOGCMD=1".encode(), 1,
                                                    "CMD[LOGCMD] ", 0)
            if self.Port.Dfu.m_i_upgrade_flag == 1:
                self.Port.Dfu.portUpTaskStop(
                    "Abnormal communication, task stop.", g_errno_d['FAIL'])

    def __subTask_SendUp_Retry(self, data, limit, msg_prefix):
        data = self.m_s_cmd_prefix.encode() + data
        for i in range(0, limit):
            self.Port.sendMsg(data)
            try:
                rx_data = self.rx_buff.get(timeout=0.2)
                if (rx_data.find(self._success_tag + str(self.m_i_pack_id)) > -1):
                    return g_errno_d['OK']
                else:
                    print(rx_data)
                    # 判断是否被分包
                    try:
                        rx_data += self.rx_buff.get(timeout=0.05)
                        if (rx_data.find(self._success_tag +
                                         str(self.m_i_pack_id)) > -1):
                            return g_errno_d['OK']
                        elif (rx_data.find(self._fail_tag) > -1):
                            # 收到错误反馈，重发消息
                            print("err data:", rx_data)
                            self.uiSendReceive(msg_prefix +
                                               "return invalid,resend " +
                                               str(i + 1))
                        else:
                            # 收到无效数据,连续读一段数据，再判断
                            print("invalid data:", rx_data)
                            try:
                                for ii in range(0, 10):
                                    rx_data += self.rx_buff.get_nowait()
                            except Exception:
                                pass
                            if (rx_data.find(self._success_tag +
                                             str(self.m_i_pack_id)) > -1):
                                return g_errno_d['OK']
                            elif (rx_data.find(self._fail_tag) > -1):
                                # 收到错误反馈，重发消息
                                print("err data:", rx_data)
                                self.uiSendReceive(msg_prefix +
                                                   "return invalid,resend " +
                                                   str(i + 1))

                    except Exception:
                        self.uiSendReceive(msg_prefix +
                                           "return invalid,resend " +
                                           str(i + 1))
            except Exception:
                self.uiSendReceive(msg_prefix + ",rx timeout,resend " +
                                   str(i + 1))
        return g_errno_d['FAIL']

    def __subTask_SendAt_Retry(self, data, limit, msg_prefix, delay):
        data = self.m_s_cmd_prefix.encode() + data
        data += self.m_b_cmd_suffix
        rx_data = ""
        for i in range(0, limit):
            self.Port.sendMsg(data)
            sleep(0.01)
            try:
                sleep(delay)
                if (delay != 0):
                    try:
                        for ii in range(0, 10):
                            rx_data += self.rx_buff.get_nowait()
                    except Exception:
                        pass
                else:
                    rx_data = self.rx_buff.get(timeout=0.2)
                self.uiSendReceive(rx_data)
                if (rx_data.find("OK") > -1):
                    return g_errno_d['OK'], rx_data
                else:
                    print("invalid data:", rx_data)
                    self.uiSendReceive(msg_prefix + "return invalid,resend " +
                                       str(i + 1))
            except Exception:
                self.uiSendReceive(msg_prefix + ",rx timeout,resend " +
                                   str(i + 1))
        return g_errno_d['FAIL'], rx_data

    def __subTask_Fota_Wait_Finish(self):
        for i in range(0, 20):
            rx_data = ""
            try:
                for ii in range(0, 10):
                    rx_data += self.rx_buff.get(timeout=1)
            except Exception:
                pass
            i = rx_data.find("finish")
            if (i != -1):
                rx_data = ""
                try:
                    for ii in range(0, 10):
                        rx_data += self.rx_buff.get(timeout=1)
                except Exception:
                    pass
                return g_errno_d['OK']

        return g_errno_d['FAIL']

    def __subTask_SendCmdAbort(self):
        '''向下位机发送停止升级命令'''
        prefix_s = "CMD[Stop upgrade] "

        self.m_b_pack_data = bytes()
        data = self.getData(False, self.UP_PACK_OPCODE_ABORT)

        ret = self.__subTask_SendUp_Retry(data, 5, prefix_s)
        return ret

    def __subTask_SendUpData(self):
        '''向下位机发送升级数据'''
        prefix_s = "CMD[Up Data] "
        self.m_i_pack_id = 0
        old_val = 0

        while 1:
            ret = self.readyNextPack()
            if (ret == g_errno_d['OK']):
                data = self.getData(False, self.UP_PACK_OPCODE_DATA)
                if (self.m_i_pack_data_len != 0):
                    ret = self.__subTask_SendUp_Retry(data, 5, prefix_s)
                else:
                    ret = g_errno_d['OK']
                    return ret
                if (ret != g_errno_d['OK']):
                    break
                if old_val != self.getUpPercentage():
                    old_val = self.getUpPercentage()
                    self.Port.upBar_signal.emit(old_val)
            else:
                ret = g_errno_d['OK']
                break
        return ret

    def __subTask_SendUpEnd(self):
        '''向下位机发送升级结束'''
        prefix_s = "CMD[Up End] "

        self.m_b_pack_data = bytes()
        data = self.getData(False, self.UP_PACK_OPCODE_DATA)

        ret = self.__subTask_SendUp_Retry(data, 5, prefix_s)
        return ret

    def Task_Start(self, start_time, file_path, prefix, LF):
        # 装载升级文件
        self.loadUpFile(file_path)
        # 填充前缀
        self.m_s_cmd_prefix = prefix
        if (LF):
            self.m_b_cmd_suffix = b'\r\n'
        else:
            self.m_b_cmd_suffix = bytes()
        # 开始盒子升级任务
        self.BoxTaskStart()
